package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: DELL
 * Date: 2023-09-05
 * Time: 7:07
 */

// UDP 回显服务器
public class UDP_EchoServer {

    // 网络编程, 本质上操作的是网卡, 但是网卡操作不便, 在操作系统内核中, 提供了 ' socket " 这样的文件来抽象表示网卡
    private DatagramSocket socket = null;

    // 服务器作为一个接收方, 是被动的接收方, 因此需要指定固定的端口号, 防止客户端来寻找不到
    public UDP_EchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动 ");
        // 服务器一般需要 7 * 24 小时进行服务, 并且给很多客户端进行服务, 但客户端什么时候发来请求是不确定的, 因此使用循环进行处理
        // 只要有请求进来就进行处理
        while(true) {
            // 1. 获取客户端请求信息
            // receive 方法处理接受消息, 是一个输出型参数, 需要先构造一个 DatagramPacket 空白的对象, 让 receive 来进行填充
            // 构造一个 DatagramPacket, 需要一个 byte[], 指定该数组长度. 放入 receive 从网卡中进行获取
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            // receive 操作系统 API, 对网卡来的信息进行解析到 UPP 协议. 当客户端发来请求时就会进行解析, 没有请求来时就会进行阻塞等待
            socket.receive(requestPacket);
            // 此时的 DatagramPacket 已经获取到对应的信息了, 但是它是一个特殊的对象, 不方便处理, 因此需要将信息拿出来构成字符串
            // 构造一个字符串, 传入 requestPacket 里的字节数组, 然后从 0 到数组有效位置结束
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());

            // 2. 根据请求计算响应
            String response = process(request);

            // 3. 将响应写回客户端并返回 - 此处构造的响应对象不能为空, 而是使用响应数据来构造
            // TODO : getBytes().length 是一个字节数组的长度, 是字节长度. 而 response.length 是一个字符长度
            //        DatagramPacket 它只认字节长度, 并不认字符长度
            // getSocketAddress() 获取到请求的端口号和 IP, 必须把这两样获取到才能知道返回给那个客户端
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            // 4. 打印日志
            System.out.printf("[%s : %d] req : %s reps : %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }


    // 回显服务器, 请求是什么响应就是什么
    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UDP_EchoServer udp_echoServer = new UDP_EchoServer(11111);
        udp_echoServer.start();
    }
}
